#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Comm.hpp"
#include "Log.hpp"
#include <functional>
#include "InetAdrr.hpp"
#include "ThreadPool.hpp"
using namespace LogModule;
const uint16_t gport = 8888;
const int gsocketfd = -1;

using word_t = std::function<std::string(const std::string&)>;
using adduser_t = std::function<void(InetAddr& in)>;
using task_t = std::function<void()>;
using route_t = std::function<void(int sockfd,const std::string& message)>;
using namespace MyThreadPool;
class Server_Udp
{
public:
    Server_Udp(uint16_t port = gport)
        :_port(port),
        _socketfd(gsocketfd),
        _addr(port)
    {
    }
    
    void InitSocket()
    {
        // 创建文件？
        _socketfd = ::socket(AF_INET, SOCK_DGRAM, 0);
        if (_socketfd < 0)
        {
            LOG(LogLevel::FATAL) << "socketfd error";
            DIE(SOCKET_ERR);
        }
        // 此时我们在内核当中初始化数据了吗？ 没有，虽然sockaddr_in是系统内部提供的数据结构，但是此时我们是把他定义在了函数内部，所以是在栈区当中。
        LOG(LogLevel::INFO) << "socket create success, sockfd is: " << _socketfd;

        InetAddr local(_port);
        int n = ::bind(_socketfd, local.NetAddr(), local.NetAddrLen());

        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "bin error";
            DIE(BIND_ERR);
        }
        LOG(LogLevel::INFO) << "bind success";
    }

    void Start()
    {
        while (true)
        {
            char inbuffer[1024];
            struct sockaddr_in peer; // 输出型参数
            socklen_t len = sizeof(peer); //必须设定

            ssize_t n = ::recvfrom(_socketfd, inbuffer, sizeof(inbuffer) - 1, 0, CONV(&peer), &len);
            std::cout << "server recieve ip: " << ::inet_ntoa(peer.sin_addr) << std::endl;

            if (n > 0)
            {
                inbuffer[n] = 0;
                InetAddr cli(peer);
                _adduser(cli);

                std::string message =  inbuffer;

                //谁发了这个消息?
                LOG(LogLevel::INFO) << cli.Addr() << " #" << message;
        
                task_t task = [&]()
                { _route(_socketfd, message); };

                threadpool<task_t>::Getinstance()->Equeue(task);
            }
        }
    }
    void Rgisterservice(adduser_t adduser, route_t route)
    {
        _adduser = adduser;
        _route = route;
    }

    ~Server_Udp()
    {
        if (_socketfd > gsocketfd)
            ::close(_socketfd);
    }

private:
    int _socketfd;
    uint16_t _port;
    InetAddr _addr;
    adduser_t _adduser;
    route_t _route;
};